﻿using System;
using System.Collections.Generic;
using ComponentSystem;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using YooAsset;

namespace YooAssetsSystem
{
    /// <summary>
    /// 资源加载组件
    /// </summary>
    public partial class AssetsLoad_Com : ComponentWithId
    {
        /// <summary>
        /// 资源句柄缓存组件
        /// </summary>
        private AssetsHandleCache_Com handleCache =>
            this.GetParent<YooAssetsSystem_Entity>().GetComponent<AssetsHandleCache_Com>();
    }

    public partial class AssetsLoad_Com
    {
        #region 资源包管理

        /// <summary>
        /// 加载资源包
        /// </summary>
        /// <param name="pkgName">资源包名</param>
        /// <returns>资源包</returns>
        public ResourcePackage LoadPackage(string pkgName)
        {
            ResourcePackage package = YooAssets.GetPackage(pkgName);

            if (package == null) throw new Exception($"获取指定资源包失败 pkgName: {pkgName}");

            return package;
        }

        /// <summary>
        /// 句柄错误处理并释放
        /// </summary>
        /// <param name="handle">句柄</param>
        /// <param name="pkgName">包名</param>
        /// <param name="assetPath">资源路径</param>
        public void HandleErrToDispose(HandleBase handle, string pkgName, string assetPath)
        {
            string err = handle.LastError;
            handle.Dispose();
            throw new Exception($"加载资源失败 pkgName: {pkgName} -- assetPath: {assetPath} -- err: {err}");
        }

        /// <summary>
        /// 获取资源句柄
        /// </summary>
        /// <param name="handleType">句柄类型</param>
        /// <param name="pkgName">包名</param>
        /// <param name="assetPath">资源路径</param>
        /// <returns>资源句柄</returns>
        public HandleBase GetResHandle(Type handleType, string pkgName, string assetPath)
        {
            return handleCache.GetHandle(handleType, pkgName, assetPath);
        }

        /// <summary>
        /// 释放资源接口
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">包名</param>
        /// <param name="handleType">句柄类型</param>
        public void ReleaseAsset(string assetName, string pkgName = "", Type handleType = null)
        {
            handleType ??= typeof(AssetHandle);
            bool success = handleCache.ReleaseHandle(handleType, pkgName, assetName);

            if (!success)
            {
                Debug.LogWarning($"释放失败: {assetName} 可能未加载或已释放");
            }
        }

        /// <summary>
        /// 强制释放所有同名资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">包名</param>
        public void ForceReleaseAll(string assetName, string pkgName = "")
        {
            handleCache.ReleaseAll(assetName, pkgName);
        }

        #endregion

        #region 功能函数

        /// <summary>
        /// 检查资源定位地址是否有效
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">包名</param>
        /// <param name="act">回调函数</param>
        /// <returns>是否有效</returns>
        public bool CheckLocationValid(string assetName, string pkgName = "", UnityAction<bool> act = null)
        {
            bool checkValid = false;

            if (string.IsNullOrEmpty(pkgName)) checkValid = YooAssets.CheckLocationValid(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                checkValid = package.CheckLocationValid(assetName);
            }

            act?.Invoke(checkValid);

            return checkValid;
        }

        #endregion

        #region 函数 加载

        /// <summary>
        /// 同步加载 通用加载 指定包中的指定资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">资源包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型 - UnityEngine支持的类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public T LoadAssets_Sync<T>(string assetName, string pkgName = "", UnityAction<T> act = null) where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            AssetHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAssetSync<T>(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadAssetSync<T>(assetName);
            }

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            T loadedAsset = assetHandle.AssetObject as T;

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// 异步加载 通用加载 指定包中的指定资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">资源包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型 - UnityEngine支持的类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public async UniTask<T> LoadAssets_Async<T>(string assetName, string pkgName = "", UnityAction<T> act = null) where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            AssetHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAssetAsync<T>(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadAssetAsync<T>(assetName);
            }

            await assetHandle.ToUniTask();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            T loadedAsset = assetHandle.AssetObject as T;

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// 异步加载 原始文件
        /// </summary>
        /// <typeparam name="T">资源类型 - UnityEngine支持的类型</typeparam>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">所在包名</param>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public async UniTask LoadRawFileAsync<T>(string assetName, string pkgName = "") where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            RawFileHandle _bundleHandle = null;

            if (string.IsNullOrEmpty(pkgName)) _bundleHandle = YooAssets.LoadRawFileAsync(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                _bundleHandle = package.LoadRawFileAsync(assetName);
            }

            await _bundleHandle.ToUniTask();
        }


        /// <summary>
        /// 同步加载 通用加载 子资源对象资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">资源包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型 - UnityEngine支持的类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public List<T> LoadSubAssets_Sync<T>(string assetName, string pkgName = "", UnityAction<List<T>> act = null) where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            SubAssetsHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadSubAssetsSync<T>(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadSubAssetsSync<T>(assetName);
            }

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            List<T> loadedAsset = assetHandle.ToList<T>();

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// 异步加载 通用加载 子资源对象资源
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">资源包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型 - UnityEngine支持的类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public async UniTask<List<T>> LoadSubAssets_Async<T>(string assetName, string pkgName = "", UnityAction<List<T>> act = null) where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName} -- assetName: {assetName}");

            SubAssetsHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadSubAssetsAsync<T>(assetName);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadSubAssetsAsync<T>(assetName);
            }

            await assetHandle.ToUniTask();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
                return null;
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            List<T> loadedAsset = assetHandle.ToList<T>();

            act?.Invoke(loadedAsset);

            return loadedAsset;
        }

        /// <summary>
        /// 同步加载 通用加载 资源包中指定路径的指定类型所有资源
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="pkgName">包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public List<T> LoadAllOneAssets_Sync<T>(string assetPath, string pkgName = "", UnityAction<List<T>> act = null)
            where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName}");

            AllAssetsHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAllAssetsSync<T>(assetPath);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadAllAssetsSync<T>(assetPath);
            }

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetPath);
                return null;
            }

            List<T> resList = new List<T>();

            foreach (var assetObj in assetHandle.AllAssetObjects)
            {
                if (assetObj is T tObject)
                {
                    resList.Add(tObject);
                }
            }

            act?.Invoke(resList);

            return resList;

        }

        /// <summary>
        /// 异步加载 通用加载 资源包中指定路径的指定类型所有资源
        /// </summary>
        /// <param name="assetPath">资源路径</param>
        /// <param name="pkgName">包名</param>
        /// <param name="act">回调函数</param>
        /// <typeparam name="T">资源类型</typeparam>
        /// <returns>加载到的对应泛型资源</returns>
        /// <exception cref="TypeLoadException">资源类型 异常</exception>
        public async UniTask<List<T>> LoadAllOneAssets_Async<T>(string assetPath, string pkgName = "", UnityAction<List<T>> act = null)
            where T : UnityEngine.Object
        {
            if (!typeof(T).IsSubclassOf(typeof(UnityEngine.Object)))
                throw new TypeLoadException($"资源加载了不支持的资源类型 {typeof(T).Name} -- pkgName: {pkgName}");

            AllAssetsHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadAllAssetsAsync<T>(assetPath);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadAllAssetsAsync<T>(assetPath);
            }

            await assetHandle.ToUniTask();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetPath);
                return null;
            }

            List<T> resList = new List<T>();

            foreach (var assetObj in assetHandle.AllAssetObjects)
            {
                if (assetObj is T tObject)
                {
                    resList.Add(tObject);
                }
            }

            act?.Invoke(resList);

            return resList;

        }

        #endregion

        #region 加载场景

        /// <summary>
        /// 同步加载 场景
        /// </summary>
        /// <param name="sceneName">场景名称</param>
        /// <param name="loadMode">加载模式</param>
        /// <param name="processing">进度回调</param>
        /// <param name="pkgName">包名</param>
        /// <param name="onComplete">完成回调</param>
        /// <returns>加载到的场景</returns>
        public Scene LoadScene_Sync(string assetName, LoadSceneMode loadMode = default, UnityAction<float> processing = null, string pkgName = "", UnityAction<Scene> onComplete = null)
        {
            SceneHandle assetHandle = null;

            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadSceneSync(assetName, loadMode);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadSceneSync(assetName, loadMode);
            }

            // 同步加载完成后直接激活场景
            assetHandle.ActivateScene();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            Scene scene = assetHandle.SceneObject;

            if (assetHandle.Status == EOperationStatus.Succeed)
                onComplete?.Invoke(scene);

            return scene;
        }

        /// <summary>
        /// 异步加载 场景
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadMode">加载模式</param>
        /// <param name="processing">进度回调</param>
        /// <param name="pkgName">包名</param>
        /// <param name="onComplete">完成回调</param>
        /// <returns>加载到的场景</returns>
        /// <exception cref="Exception">异常</exception>
        public async UniTask<Scene> LoadScene_Async(string assetName, LoadSceneMode loadMode = default, UnityAction<float> processing = null, string pkgName = "", UnityAction<Scene> onComplete = null)
        {
            SceneHandle assetHandle = null;

            Debug.Log($"LoadScene_Async assetName: {assetName} loadMode: {loadMode} pkgName: {pkgName}");
            if (string.IsNullOrEmpty(pkgName)) assetHandle = YooAssets.LoadSceneAsync(assetName, loadMode);
            else
            {
                ResourcePackage package = LoadPackage(pkgName);
                assetHandle = package.LoadSceneAsync(assetName, loadMode);
            }

            while (!assetHandle.IsDone)
            {
                processing?.Invoke(assetHandle.GetDownloadStatus().Progress);
                await UniTask.Yield();
            }

            await assetHandle.ToUniTask();

            assetHandle.ActivateScene();

            if (assetHandle.Status != EOperationStatus.Succeed)
            {
                HandleErrToDispose(assetHandle, pkgName, assetName);
            }

            this.handleCache.AddHandle(assetHandle, pkgName, assetName);

            Scene scene = assetHandle.SceneObject;

            if (assetHandle.Status == EOperationStatus.Succeed)
                onComplete?.Invoke(scene);

            return scene;
        }

        /// <summary>
        /// 异步卸载 场景
        /// </summary>
        /// <param name="assetName">资源名称</param>
        /// <param name="pkgName">包名</param>
        /// <returns>是否卸载成功</returns>
        public async UniTask<bool> UnLoadScene_Async(string assetName, string pkgName = "")
        {
            Type type = typeof(SceneHandle);
            SceneHandle assetHandle = GetResHandle(type, pkgName, assetName) as SceneHandle;
            if (assetHandle != null)
            {
                if (assetHandle.SceneName == assetName)
                {
                    await assetHandle.UnloadAsync();
                    return true;
                }
            }

            return false;
        }


        #endregion
    }

    /// <summary>
    /// SubAssetsHandle 扩展方法
    /// </summary>
    public static class SubAssetsHandleExtensions
    {
        /// <summary>
        /// 将 SubAssetObjects 转换为指定类型的 List
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="handle">SubAssetsHandle</param>
        /// <returns>转换后的 List</returns>
        public static List<T> ToList<T>(this SubAssetsHandle handle) where T : UnityEngine.Object
        {
            List<T> result = new List<T>();
            foreach (var obj in handle.SubAssetObjects)
            {
                if (obj is T tObj)
                {
                    result.Add(tObj);
                }
            }
            return result;
        }
    }
}